﻿#include "stdafx.h"

void Ex_SetLastError(INT nError) {
  if (nError != 0) {
    Thread_EnterCriticalSection(g_Li.csError);
    g_Li.dwError = nError;
    Thread_LeaveCriticalSection(g_Li.csError);
  }
}

INT Ex_GetLastError() { return g_Li.dwError; }

BOOL Ex_Init(HINSTANCE hInstance, DWORD dwGlobalFlags, HCURSOR hDefaultCursor,
             LPCWSTR lpszDefaultClassName, LPVOID lpDefaultTheme,
             size_t dwDefaultThemeLen, LPCWSTR lpDefaultFontFace,
             size_t dwDefaultFontSize) {
  CoInitialize(NULL);
  g_Li.csError = Thread_InitializeCriticalSection();

  g_Li.hInstance = hInstance;
  g_Li.dwFlags = dwGlobalFlags;
  if (hDefaultCursor == 0) {
    g_Li.hCursor = LoadCursorW(0, IDC_ARROW);
  } else {
    g_Li.hCursor = hDefaultCursor;
  }
  if (lpszDefaultClassName == NULL) {
    lpszDefaultClassName = L"Ex_DirectUI";
  }

  TCHAR szFilePath[MAX_PATH + 1];
  GetModuleFileName(NULL, szFilePath, MAX_PATH);  // 取自身完整路径

  g_Li.hIcon = ExtractIconW(g_Li.hInstance, szFilePath, 0);
  g_Li.hIconsm = ExtractIconW(g_Li.hInstance, szFilePath, 0);

  g_Li.atomClassName =
      Ex_WndRegisterClass(lpszDefaultClassName, NULL, NULL, NULL);

  g_Li.dwMessage = RegisterWindowMessageW(L"Ex_DirectUI");
  g_Li.hTableClass = HashTable_Create(47, pfnDefaultFreeData);
  g_Li.hTableFont = HashTable_Create(17, pfnDefaultFreeFont);
  g_Li.hTableLayout = HashTable_Create(17, NULL);
  g_Li.hMemPoolMsg = MemPool_Create(256, sizeof(mempoolmsg_s), 0);
  g_Li.hHandles = _handle_init();
  g_Li.dwClickTime = GetDoubleClickTime() * 2;

  g_Li.aryColorsAtom = {ATOM_BACKGROUND_COLOR,
                        ATOM_COLOR_BACKGROUND,
                        ATOM_BORDER_COLOR,
                        ATOM_COLOR_BORDER,
                        ATOM_COLOR,
                        ATOM_COLOR_NORMAL,
                        ATOM_COLOR_HOVER,
                        ATOM_COLOR_DOWN,
                        ATOM_COLOR_FOCUS,
                        ATOM_COLOR_CHECKED,
                        ATOM_COLOR_SELECTED,
                        ATOM_COLOR_HOT,
                        ATOM_COLOR_VISTED,
                        ATOM_COLOR_SHADOW};
  g_Li.aryColorsOffset = {
      offsetof(obj_s, crBackground_), offsetof(obj_s, crBackground_),
      offsetof(obj_s, crBorder_),     offsetof(obj_s, crBorder_),
      offsetof(obj_s, crNormal_),     offsetof(obj_s, crNormal_),
      offsetof(obj_s, crHover_),      offsetof(obj_s, crDown_),
      offsetof(obj_s, crFocus_),      offsetof(obj_s, crChecked_),
      offsetof(obj_s, crSelected_),   offsetof(obj_s, crHot_),
      offsetof(obj_s, crVisted_),     offsetof(obj_s, crShadow_)};
  g_Li.hModuleUser = GetModuleHandleW(L"user32.dll");
  g_Ri.hRiched20 = LoadLibraryW(L"Msftedit.dll");
  // g_Ri.hRiched20 = LoadLibraryW(L"Riched20.dll");
  LPVOID i = Ex_MemAlloc(64);
  INT len;
  len = LoadStringW(g_Li.hModuleUser, 900, (LPWSTR)i, 64);
  g_Li.lpStrMin = StrDupW((LPWSTR)i);
  RtlZeroMemory(i, 64);
  len = LoadStringW(g_Li.hModuleUser, 901, (LPWSTR)i, 64);
  g_Li.lpStrMax = StrDupW((LPWSTR)i);
  RtlZeroMemory(i, 64);
  len = LoadStringW(g_Li.hModuleUser, 902, (LPWSTR)i, 64);
  g_Li.lpStrResMin = StrDupW((LPWSTR)i);
  RtlZeroMemory(i, 64);
  len = LoadStringW(g_Li.hModuleUser, 903, (LPWSTR)i, 64);
  g_Li.lpStrResMax = StrDupW((LPWSTR)i);
  RtlZeroMemory(i, 64);
  len = LoadStringW(g_Li.hModuleUser, 904, (LPWSTR)i, 64);
  g_Li.lpStrHelp = StrDupW((LPWSTR)i);
  RtlZeroMemory(i, 64);
  len = LoadStringW(g_Li.hModuleUser, 905, (LPWSTR)i, 64);
  g_Li.lpStrClose = StrDupW((LPWSTR)i);
  Ex_MemFree(i);
  INT nError = 0;
  _canvas_init(&nError);
  SetProcessDPIAware();

  g_Li.DpiX = 1;
  g_Li.DpiY = 1;
  HDC dc = GetDC(NULL);
  g_Li.DpiX_Real = (FLOAT)GetDeviceCaps(dc, 88) / 96;
  g_Li.DpiY_Real = (FLOAT)GetDeviceCaps(dc, 90) / 96;
  ReleaseDC(NULL, dc);
  if (g_Li.DpiX_Real == 1 && g_Li.DpiY_Real == 1) {
    Flag_Del(ENGINE_FLAG_DPI_ENABLE);
  } else {
    if ((dwGlobalFlags & ENGINE_FLAG_DPI_ENABLE) == ENGINE_FLAG_DPI_ENABLE) {
      g_Li.DpiX = g_Li.DpiX_Real;
      g_Li.DpiY = g_Li.DpiY_Real;
    }
  }
  g_Li.lpLogFontDefault = (LOGFONTW*)Ex_MemAlloc(sizeof(LOGFONTW));
  SystemParametersInfoW(31, sizeof(LOGFONTW), g_Li.lpLogFontDefault, 0);
  if (lpDefaultFontFace) {
    wcsncpy(g_Li.lpLogFontDefault->lfFaceName, lpDefaultFontFace, LF_FACESIZE);
  }
  if (dwDefaultFontSize > 0) {
    g_Li.lpLogFontDefault->lfHeight = -(LONG)dwDefaultFontSize;
  }
  if (!Flag_Query(ENGINE_FLAG_DPI_ENABLE)) {
    g_Li.lpLogFontDefault->lfHeight =
        (FLOAT)g_Li.lpLogFontDefault->lfHeight / g_Li.DpiY_Real;
  }

  _object_init();

  g_Li.aryThemes.clear();
  Ex_ThemeLoadFromMemory(lpDefaultTheme, dwDefaultThemeLen, 0, 0, TRUE);
  _layout_init();
  g_Li.atomSysShadow = Ex_WndRegisterClass(L"SysShadow", 0, 0, 0);

  g_Li.hHookMsgBox =
      SetWindowsHookExW(WH_CBT, (HOOKPROC)_hook_proc, 0, GetCurrentThreadId());

  nError = g_Ri.pDWriteFactory->RegisterFontFileLoader(
      ExLazySingleton<ExFontFileLoader>::GetInstance());

  nError = g_Ri.pDWriteFactory->RegisterFontCollectionLoader(
      ExLazySingleton<ExFontCollectionLoader>::GetInstance());

  Ex_SetLastError(nError);
  return nError == 0;
}

void Ex_UnInit() {
  UnhookWindowsHookEx(g_Li.hHookMsgBox);
  Ex_MemFree((LPVOID)g_Li.lpStrMin);
  Ex_MemFree((LPVOID)g_Li.lpStrMax);
  Ex_MemFree((LPVOID)g_Li.lpStrResMin);
  Ex_MemFree((LPVOID)g_Li.lpStrResMax);
  Ex_MemFree((LPVOID)g_Li.lpStrClose);
  Ex_MemFree((LPVOID)g_Li.lpStrHelp);
  Ex_ThemeFree(g_Li.hThemeDefault);
  Ex_MemFree(g_Li.lpLogFontDefault);

  g_Ri.pDWriteFactory->UnregisterFontFileLoader(
      ExLazySingleton<ExFontFileLoader>::Instance());
  g_Ri.pDWriteFactory->UnregisterFontCollectionLoader(
      ExLazySingleton<ExFontCollectionLoader>::Instance());
  // 释放字体集加载器和字体文件加载器
  ExLazySingleton<ExFontCollectionLoader>::ClearInstance(true);
  ExLazySingleton<ExFontFileLoader>::ClearInstance(true);
  _canvas_uninit();
  if (g_Li.hMenuEdit)
      Ex_MenuDestroy(g_Li.hMenuEdit);
  if (g_Li.hMenuVS)
      Ex_MenuDestroy(g_Li.hMenuVS);
  if (g_Li.hMenuHS)
      Ex_MenuDestroy(g_Li.hMenuHS);
  {
#ifdef _DEBUG
      if (!g_Li.hHandles) return;

      int totalUnreleased = 0;
      int unreleasedByType[9] = { 0 }; // type 1~8
      mempoolheader_s* pEntry = (mempoolheader_s*)g_Li.hHandles->pBase;
      size_t nBlock = g_Li.hHandles->nBlockSize;
      for (size_t p = (size_t)pEntry; p < (size_t)pEntry + nBlock * g_Li.hHandles->nMax; p += nBlock) {
          if ((((mempoolheader_s*)p)->dwFlags & mpbf_used) == mpbf_used) {
              INT& handle = ((mempoolheader_s*)p)->lParam; // lParam 存的是 EXHANDLE
              DWORD magic = (handle >> 24) & 0xFF;
              DWORD type = (handle >> 18) & 0x3F;
              DWORD index = (handle >> 2) & 0xFFFF;
              if (magic == 44 && index >= 1 && index <= 65536 && type >= 1 && type <= 8) {
                  totalUnreleased++;
                  unreleasedByType[type]++;
              }
          }
      }

      {
          wchar_t buffer[512];
          swprintf_s(buffer, L"* [句柄监测]: ExDUIR句柄未释放数: %d\n"
              L"* 引擎:%d, 组件:%d, 画布:%d, 图片:%d, 布局:%d, 路径:%d, SVG:%d, 菜单:%d\n",
              totalUnreleased,
              unreleasedByType[1], unreleasedByType[2], unreleasedByType[3],
              unreleasedByType[4], unreleasedByType[5], unreleasedByType[6],
              unreleasedByType[7], unreleasedByType[8]);
          OutputDebugStringW(buffer);
      }
#endif // DEBUG
  }

  _handle_uninit(g_Li.hHandles);
  HashTable_Destroy(g_Li.hTableClass);
  HashTable_Destroy(g_Li.hTableFont);
  HashTable_Destroy(g_Li.hTableLayout);
  MemPool_Destroy(g_Li.hMemPoolMsg);
  Thread_DeleteCriticalSection(g_Li.csError);
  FreeLibrary(g_Ri.hRiched20);
  DestroyIcon(g_Li.hIcon);
  DestroyIcon(g_Li.hIconsm);
  DestroyCursor(g_Li.hCursor);
  CoUninitialize();
}

FLOAT Ex_Scale(FLOAT n) {
  if (g_Li.DpiX > 1) {
    n = round(n * g_Li.DpiX);
  }
  return n;
}

void Ex_Sleep(INT us) {
  LARGE_INTEGER li;
  li.QuadPart = (LONGLONG)(-10 * us);
  HANDLE hTimer = CreateWaitableTimerW(0, 0, 0);
  if (hTimer) {
    SetWaitableTimer(hTimer, &li, 0, 0, 0, 0);
    while (MsgWaitForMultipleObjects(1, &hTimer, 0, -1, 255) != 0) {
      MSG msg;
      while (PeekMessageW(&msg, 0, 0, 0, 1)) {
        TranslateMessage(&msg);
        DispatchMessageW(&msg);
      }
    }
    CloseHandle(hTimer);
  }
}

EXATOM Ex_Atom(LPCWSTR lptstring) {
  INT len = lstrlenW(lptstring);
  EXATOM ret = 1;
  if (len > 0) {
    PWSTR buff = (PWSTR)StrDupW(lptstring);
    CharLowerW(buff);
    ret = Crc32_Addr((UCHAR*)buff, len * 2);
    Ex_MemFree(buff);
  }
  return ret;
}

LPVOID Ex_AllocBuffer(size_t dwSize) { return Ex_MemAlloc(dwSize); }

BOOL Ex_FreeBuffer(LPVOID lpBuffer) { return Ex_MemFree(lpBuffer); }

LPVOID Ex_LoadImageFromMemory(LPVOID lpData, size_t dwLen, INT uType,
                              INT nIndex) {
  LPVOID ret = 0;
  if (!IsBadReadPtr(lpData, dwLen)) {
    LPVOID hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, dwLen);
    if (hMem) {
      LPVOID lpMem = GlobalLock(hMem);
      if (lpMem) {
        RtlMoveMemory(lpMem, lpData, dwLen);
        GlobalUnlock(hMem);
        if (uType == IMAGE_ICON || uType == IMAGE_CURSOR) {
          if (nIndex == 0) nIndex = 1;
          WORD s = ((ICONDIR*)lpData)->idCount;
          if (s >= nIndex) {
            short type = ((ICONDIR*)lpData)->idType;
            BOOL bIcon = (type == IMAGE_ICON);
            LPVOID offset = (LPVOID)((size_t)lpData + 6 +
                                     (nIndex - 1) * sizeof(ICONDIRENTRY));
            LPVOID tmp = lpMem;
            if (!bIcon) {
              __set_short(lpMem, 0, ((ICONDIRENTRY*)offset)->wBitCount);
              tmp = (LPVOID)((size_t)tmp + 4);
            }
            RtlMoveMemory(tmp,
                          (LPVOID)((size_t)lpData +
                                   ((ICONDIRENTRY*)offset)->dwImageOffset),
                          ((ICONDIRENTRY*)offset)->dwBytesInRes);
            CHAR b1 = ((ICONDIRENTRY*)offset)->bWidth;
            CHAR b2 = ((ICONDIRENTRY*)offset)->bHeight;
            HICON hicon = CreateIconFromResourceEx(
                (PBYTE)lpMem, ((ICONDIRENTRY*)offset)->dwBytesInRes, bIcon,
                196608, b1, b2, 0);
            ret = CopyImage(hicon, uType, 0, 0, LR_COPYRETURNORG);
          }
        } else {
          LPSTREAM lpStream;
          if (CreateStreamOnHGlobal(hMem, FALSE, &lpStream)) {
            LPVOID lpObj = nullptr;
            if (OleLoadPicture(lpStream, dwLen, TRUE, IID_IPicture, &lpObj) ==
                0) {
              OLE_HANDLE hImg = 0;
              ((IPicture*)lpObj)->get_Handle(&hImg);
              SHORT objType = 0;
              ((IPicture*)lpObj)->get_Type(&objType);
              if (objType == uType) {
                ret = CopyImage((HANDLE)hImg, uType, 0, 0, LR_COPYRETURNORG);
              }
              ((IPicture*)lpObj)->Release();
            }
            lpStream->Release();
          }
        }
      }
      GlobalFree(hMem);
    }
  }
  return ret;
}

BOOL Ex_LoadBitMapFromMemory(LPVOID lpData, size_t dwLen, HBITMAP* retBitMap) {
  INT nError = 0;
  IWICStream* pIWICStream = nullptr;
  HRESULT hr = g_Ri.pWICFactory->CreateStream(&pIWICStream);
  BOOL ret = FALSE;
  if (hr == 0) {
    hr = pIWICStream->InitializeFromMemory((WICInProcPointer)lpData, dwLen);
    if (hr == 0) {
      IWICBitmapDecoder* pDecoder = NULL;
      hr = g_Ri.pWICFactory->CreateDecoderFromStream(
          pIWICStream, NULL, WICDecodeMetadataCacheOnLoad, &pDecoder);
      if (hr == 0) {
        IWICBitmapFrameDecode* pSource = NULL;
        hr = pDecoder->GetFrame(0, &pSource);
        IWICFormatConverter* pConverter = NULL;
        hr = g_Ri.pWICFactory->CreateFormatConverter(&pConverter);
        pConverter->Initialize(
            (IWICBitmap*)pSource, GUID_WICPixelFormat32bppPBGRA,
            WICBitmapDitherTypeNone, NULL, 0.0, WICBitmapPaletteTypeCustom);
        UINT width = 0, height = 0;
        pConverter->GetSize(&width, &height);
        BYTE* buffer = new BYTE[width * height * 4];
        pConverter->CopyPixels(0, width * 4, width * height * 4, buffer);
        if (retBitMap) {
          *retBitMap = CreateBitmap(width, height, 1, 32, buffer);
          ret = TRUE;
        }
        delete[] buffer;
        pSource->Release();
        pConverter->Release();
      }
      pDecoder->Release();
    }
    pIWICStream->Release();
  }
  return ret;
}

LPVOID Ex_MemAlloc(size_t dwSize, INT dwFlags) {
  return HeapAlloc(GetProcessHeap(), 8, dwSize);
}

LPVOID Ex_MemReAlloc(LPVOID hMem, size_t dwSize) {
  return HeapReAlloc(GetProcessHeap(), 8, hMem, dwSize);
}

BOOL Ex_MemFree(LPVOID hMem) {
  if (hMem != nullptr) {
    return HeapFree(GetProcessHeap(), 0, hMem);
  }
  return FALSE;
}

BOOL Ex_ReadFile(LPCWSTR filePath, std::vector<CHAR>* retData) {
  BOOL fOK = FALSE;
  if (filePath != L"") {
    HANDLE hFile = CreateFileW(filePath, GENERIC_READ, FILE_SHARE_READ, NULL,
                               OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile != INVALID_HANDLE_VALUE) {
      DWORD nSize = GetFileSize(hFile, NULL);
      if (nSize != INVALID_FILE_SIZE) {
        (*retData).resize(nSize);
        fOK = ReadFile(hFile, &(*retData)[0], nSize, &nSize, NULL);
      }
      CloseHandle(hFile);
    }
  }
  return fOK;
}

BOOL Ex_WriteFile(LPCWSTR szFileName, LPVOID pData, size_t npDataSize) {
  if (szFileName[0] == L'\0' || npDataSize < 0) return false;
  FILE* out = _tfopen(szFileName, _T("wb"));
  if (out == NULL) return false;
  const bool blpSucceeded = (fwrite(pData, 1, npDataSize, out) == npDataSize);
  fclose(out);
  return blpSucceeded;
}

/* 读取RC资源 */
BOOL Ex_ReadResSource(WORD lpname, LPCWSTR lpType, std::vector<CHAR>* retData) {
  BOOL fOK = FALSE;
  /* 检查参数有效性 */
  if (lpname) {
    /* 查找资源 */
    HRSRC hRsrc = FindResourceW(NULL, MAKEINTRESOURCE(lpname), lpType);
    if (NULL == hRsrc) return (FALSE);

    /* 获取资源的大小 */
    DWORD dwSize = SizeofResource(NULL, hRsrc);
    if (dwSize == 0) {
      return (FALSE);
    }

    /* 加载资源 */
    HGLOBAL hGlobal = LoadResource(NULL, hRsrc);
    if (hGlobal == 0) {
      return (FALSE);
    }

    /* 锁定资源 */
    LPVOID pBuffer = LockResource(hGlobal);
    if (pBuffer == NULL) {
      return (FALSE);
    }

    (*retData).resize(dwSize);
    RtlMoveMemory((*retData).data(), pBuffer, dwSize);
    fOK = TRUE;
    /*
     * 在资源使用完毕后我们可以不需要使用
     * UnlockResource和FreeResource来手动地释放资源
     * 因为它们都是16位Windows遗留下来的，在Win32中，在使用完毕后系统会自动回收。
     */
    UnlockResource(hGlobal);
    FreeResource(hGlobal);
  }
  return (fOK);
}

// 取系统缩放比例
DOUBLE Ex_DUIGetSystemDpi() {
  HDC desktopDc = GetDC(NULL);
  DOUBLE dpiA = (DOUBLE)GetDeviceCaps(desktopDc, DESKTOPHORZRES) /
                GetDeviceCaps(desktopDc, HORZRES);
  DOUBLE dpiB = (DOUBLE)GetDeviceCaps(desktopDc, LOGPIXELSX) / 96;
  ReleaseDC(NULL, desktopDc);
  if (dpiA == 1) {
    return (dpiB);
  } else if (dpiB == 1) {
    return (dpiA);
  } else if (dpiA == dpiB) {
    return (dpiA);
  } else {
    return 1;
  }
}

void* Ex_GetIDWriteFactory()
{
    return g_Ri.pDWriteFactory;

}
void* Ex_GetD2D1Factory1()
{
    return g_Ri.pD2Dfactory;
}
void* Ex_GetD3DDevice()
{
    return g_Ri.pD3DDevice;
}
void* Ex_GetD3DDevice1()
{
    return g_Ri.pD3DDevice1;
}
void* Ex_GetD2DDevice()
{
    return g_Ri.pD2DDevice;
}
void* Ex_GetDXGIDevice()
{
    return g_Ri.pDXGIDevice;
}

void* Ex_GetD3DDeviceContext()
{
    return g_Ri.pD3DDeviceContext;
}